#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <termios.h>     //包含了6818开发板的 串口配置接口文件
#include <linux/input.h> //包含了 输入事件信息的 描述结构体
#include <stdlib.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdbool.h>

int lcd_fd; // LCD显示文件的标识符
int *plcd;  // LCD映射内存空间
int bmp_fd; // bmp文件标识符
int Red = 0xff0000;
int White = 0xffffff;
int Black = 0x000000;
int orgian = 0xff7f50;
int cmp_fd = 0;
struct input_event ev; // 定义输入事件类型结构体变量，来存储输入数据
unsigned char rbuf[256] = {0};
unsigned char wbuf[256] = {0};

/*
open_lcd: 打开并映射lcd
Param: None
Retval:
0，打开失败
1，打开成功
*/
int open_lcd(void)
{
    lcd_fd = open("/dev/fb0", O_RDWR);
    if (lcd_fd == -1)
    {
        printf("lcd open failed\n");
        return 0;
    }

    // 内存映射，从头开始
    plcd = (int *)mmap(NULL, 800 * 480 * 32 / 8, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
    if (plcd == NULL)
    {
        printf("lcd mmap failed\n");
        close(lcd_fd);
        return 0;
    }
    printf("lcd open success\n");
    return 1;
}
/*
close_lcd: 关闭LCD
Param:
None
Retval:
None
*/
void close_lcd(void)
{
    int flag;
    flag = munmap(plcd, 800 * 480 * 4);
    if (flag == -1)
    {
        printf("munmap lcd failed\n");
    }
    flag = close(lcd_fd);
    if (flag == -1)
    {
        printf("close lcd failed\n");
    }
}
/*
show_point_color: 显示一个像素点的颜色
Param:
x,y: 要显示颜色的 坐标点
color: 要显示的颜色
Retval:
None
*/
void show_point_color(int x, int y, int color)
{
    if (x > 800 || x < 0 || y > 480 || y < 0)
    {
        printf("超出坐标范围\n");
        return;
    }
    *(plcd + 800 * y + x) = color;
}
/*
show_color: 显示指定范围大小的颜色
Param:
x0,y0: 表示从什么地方开始显示颜色
x,y: 到什么地方 结束显示颜色
color: 要显示的颜色
Retval:
None
*/
void show_color(int x0, int y0, int x, int y, int color)
{
    int X, Y;
    if (x > 800 || x < 0 || y > 480 || y < 0 || x0 > 800 || x0 < 0 ||
        y0 > 480 || y0 < 0)
    {
        printf("超出坐标范围\n");
        return;
    }
    for (Y = y0; Y <= y; Y++)
    {
        for (X = x0; X <= x; X++)
        {
            show_point_color(X, Y, color);
        }
    }
}
// 显示bmp图片
void show_bmp(const char *path)
{
    char data[10] = {0};
    int flag = 0;
    int h;     // 位高
    int w;     // 位宽
    int depth; // 色深
    int bmp_color;
    int tc_value;    // 系统填充数据
    char a, r, g, b; // 定义各基色变量，方便进行移位合并处理
    int line_data;
    bmp_fd = open(path, O_RDWR);
    if (bmp_fd == -1)
    {
        printf("bmp_fd open failed\n");
        return;
    }
    // 读取位图宽度数据
    // 移动光标到0x12的位置开始读取
    flag = lseek(bmp_fd, 0x12, SEEK_SET); // 从文件开头偏移0x12的距离
    if (flag == -1)
    {
        printf("lseek 0x12 failed\n");
        close(bmp_fd); // 关闭已打开的bmp文件
        return;
    }
    // 读取位图宽度数据
    flag = read(bmp_fd, data, 4);
    if (flag == -1)
    {
        printf("read w data sfailed\n");
        close(bmp_fd); // 关闭已打开的bmp文件
        return;
    }
    w = data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24;
    // 因为是小端存储，所以需要进行左移操作
    printf("位图宽度为: %d\n", abs(w));
    // 读取位图高度数据
    // 移动光标到0x16的位置开始读取
    flag = lseek(bmp_fd, 0x16, SEEK_SET); // 从文件开头偏移0x16的距离
    if (flag == -1)
    {
        printf("lseek 0x16 failed\n");
        close(bmp_fd); // 关闭已打开的bmp文件
        return;
    }
    // 读取位图宽度数据
    flag = read(bmp_fd, data, 4);
    if (flag == -1)
    {
        printf("read h data sfailed\n");
        close(bmp_fd); // 关闭已打开的bmp文件
        return;
    }
    h = data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24;
    // 因为是小端存储，所以需要进行左移操作
    printf("位图高度为: %d\n", abs(h));
    // 读取位图色深数据
    // 移动光标到0x1C的位置开始读取
    flag = lseek(bmp_fd, 0x1C, SEEK_SET); // 从文件开头偏移0x1C的距离
    if (flag == -1)
    {
        printf("lseek 0x1C failed\n");
        close(bmp_fd); // 关闭已打开的bmp文件
        return;
    }
    // 读取位图宽度数据
    flag = read(bmp_fd, data, 2);
    if (flag == -1)
    {
        printf("read depth data sfailed\n");
        close(bmp_fd); // 关闭已打开的bmp文件
        return;
    }
    depth = data[0] | data[1] << 8; // 因为是小端存储，所以需要进行左移操作
    printf("位图色深为: %d\n", abs(depth));
    // 显示最重要的数据是 像素数组
    // 偏移54bytes
    flag = lseek(bmp_fd, 54, SEEK_SET); // 从文件开头偏移54的距离
    if (flag == -1)
    {
        printf("lseek 54 failed\n");
        close(bmp_fd); // 关闭已打开的bmp文件
        return;
    }
    // 读取像素数组的数据
    char color_data[800 * 480 * 4 + 20];
    flag = read(bmp_fd, color_data, abs(w) * abs(h) * abs(depth) / 8); // 读取像素数组数据，将其存入到color_data中
    if (flag == -1)
    {
        printf("read color failed\n");
        close(bmp_fd); // 关闭已打开的bmp文件
        return;
    }
    printf("bmp color read success\n");
    // 考虑字节对齐的问题，需要忽略一些系统所填充的数据
    line_data = abs(w) * depth / 8; // 一行像素点所占大小
    if (line_data % 4 != 0)
    {
        tc_value = 4 - line_data; // 得到填充的数据
    }
    int x, y;
    int i = 0;
    for (y = 0; y < abs(h); y++) // 循环每一行
    {
        for (x = 0; x < abs(w); x++) // 循环每一行中的每一个像素点
        {
            b = color_data[i++];
            g = color_data[i++];
            r = color_data[i++];
            if (depth == 32)
                a = color_data[i++];
            else
                a = 0;
            bmp_color = b | g << 8 | r << 16 | a << 24; // 解析的是一个像素点的颜色值
            // printf("颜色值解析完毕，开始进行写入\n");
            /*

            位宽数据w > 0，从左往右顺序存放像素点颜色值，< 0，从右往左
            位高数据h > 0，从上往下顺序存放像素点颜色值，< 0，从下往上
            w>0? 0+x:abs(w)-1-x
            h>0? 0+y:abs(h)-1-y
            */
            // 将颜色写入映射空间，进行显示
            show_point_color((w > 0 ? 0 + x : abs(w) - 1 - x), (h > 0 ? 0 + y : abs(h) - 1 - y), bmp_color);
        }
        i += tc_value; // 跳过bmp_color数组中的填充数据
    }
    printf("图片颜色显示完毕\n");
    close(bmp_fd);
}

void *screen(void *arg)
{
    /* 解析触摸屏产生的输入数据 */
    // 1.打开触摸屏的输入事件文件
    // int cmp_fd = 0;
    // struct input_event ev; // 定义输入事件类型结构体变量，来存储输入数据
    int flag = 0;
    cmp_fd = open("/dev/input/event0", O_RDWR);
    if (cmp_fd == -1)
    {
        printf("open event0 failed\n");
        return 0;
    }
    // 2.获取触摸屏的输入数据
    while (1)
    {
        flag = read(cmp_fd, &ev, sizeof(ev)); // 读取触摸屏的输入事件数据
        if (flag == -1)
        {
            printf("read event0 failed\n");
            close(cmp_fd);
            return 0;
        }
        // 解析输入数据
        // printf("解析的数据:type: %x, code: %x, value: %d\n", ev.type,
        //        ev.code, ev.value);
    }
}

void meanwhile(void *(*thread_1)(void *), void *(*thread_2)(void *))
{
    pthread_t t1, t2; // 创建两个保存线程号的变量
    int flag = 0;
    // 创建线程1
    flag = pthread_create(&t1, NULL, thread_1, NULL);
    if (flag != 0)
    {
        printf("thread_1 create failed\n");
        return;
    }
    // 创建线程2
    flag = pthread_create(&t2, NULL, thread_2, NULL);
    if (flag != 0)
    {
        printf("thread_2 create failed\n");
        return;
    }
}

void *seen(void *buff)
{
    // 定义一个以太网地址结构体变量
    struct sockaddr_in SAddr;
    int sockfd; // 套接字文件描述符
    int connfd; // 通信链接文件描述符
    ssize_t rsize = 0;
    // 读、写数据分开
    // unsigned char rbuf[256] = {0};
    // unsigned char wbuf[256] = {0};
    /* 1.创建套接字(创建网络通信接口) */
    // 创建了一个 IPV4的流式套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) // 判断有无创建成功
    {
        printf("Client socket create failed\n");
        return 0;
    }
    // 2. 客户端发起连接请求
    // 首先给以太网地址结构体变量 赋值
    SAddr.sin_family = AF_INET; // 指定协议族为IPV4
    // 指定连接的服务器的端口号
    SAddr.sin_port = htons(8899);
    // 指定连接的服务器的IP地址
    SAddr.sin_addr.s_addr = inet_addr("192.168.31.17");
    // 链接服务器
    connfd = connect(sockfd, (struct sockaddr *)&SAddr, sizeof(SAddr));
    if (connfd == -1)
    {
        printf("connect failed\n");
        close(sockfd);
        return 0;
    }
    // 连接服务器成功

    // 与服务器进行数据收发
    while (1)
    {
        // read(sockfd) 接收服务器的数据
        memset(rbuf, 0, sizeof(rbuf)); // 清0 rbuf
        rsize = read(sockfd, rbuf, 256);
        if (rsize == -1) // 读取出错的情况
        {
            printf("read from Sever failed\n");
            close(sockfd);
            return 0;
        }
        else if (rsize == 0) // 没读到任何数据的情况
        {
            printf("服务器或异常退出\n");
            close(sockfd);
            return 0;
        }
        printf("从服务器接收到%ld个字节数据: %s\n", rsize, rbuf);
        // write(sockfd) 发送数据给服务器
        printf("input cmd>>");         // 提示信息，提醒用户输入要发送的数据
        fflush(stdout);                // 用冲洗函数，立即将缓冲区中的数据"input cmd>>" 输出
        memset(wbuf, 0, sizeof(wbuf)); // 清0 wbuf                        // 命令行输入要发送的数据
        getchar();
        rsize = write(sockfd, wbuf, strlen(wbuf)); // 输出wbuf中的字符串数据
        if (rsize <= 0)
        {
            printf("Client write to Sever failed\n");
            close(sockfd);
            return 0;
        }
        // 自行定义一个通信结束的标准
        // 比如，发的是bye 就结束通信
        if (strcmp(wbuf, "bye") == 0)
        {
            break;
        }
    }
    close(sockfd); // 关闭套接字
}
void hanzi_jie(void) // 结
{
    char jie1_data[24] = {0x00, 0x18, 0x3E, 0x3F, 0x38, 0x38, 0x39, 0x39, 0x39, 0x3B, 0x3F, 0x3B, 0x3B, 0x3B, 0x3B, 0x3F, 0x3B, 0x3B, 0x3B, 0x3B, 0x7F, 0x30, 0x00, 0x00}; // 保存“山”取模数据
    char jie2_data[24] = {0x00, 0x0E, 0x38, 0x71, 0xE1, 0xF9, 0xFF, 0xF9, 0xB9, 0xB9, 0xB9, 0xB9, 0xBF, 0x9E, 0x8E, 0x8E, 0x8F, 0xFF, 0xAE, 0x8E, 0x8E, 0x8E, 0x0E, 0x00};
    char jie3_data[24] = {0x00, 0x00, 0x1C, 0x9C, 0x9C, 0x98, 0x9C, 0xBF, 0xB8, 0xB0, 0xB0, 0xF0, 0xE0, 0x7C, 0x7C, 0xEE, 0xC7, 0xC0, 0xF0, 0xB0, 0x38, 0x1C, 0x00, 0x00};
    for (int j = 0; j < 24; j++) // 解析汉字取模的每一行数据
    {
        for (int i = 23; i >= 0; i--) // 仅解析一个汉字的一行数据
        {
            if (jie1_data[j] >> i & 1) // 判断当前t的第i位是否为 有效数据位
            {
                show_point_color(2 + (23 - i), 138 + j, 0);
            }
        }
    }

    for (int j = 0; j < 24; j++) // 解析汉字取模的每一行数据
    {
        for (int i = 23; i >= 0; i--) // 仅解析一个汉字的一行数据
        {
            if (jie2_data[j] >> i & 1) // 判断当前t的第i位是否为 有效数据位
            {
                show_point_color(10 + (23 - i), 138 + j, 0);
            }
        }
    }

    for (int j = 0; j < 24; j++) // 解析汉字取模的每一行数据
    {
        for (int i = 23; i >= 0; i--) // 仅解析一个汉字的一行数据
        {
            if (jie3_data[j] >> i & 1) // 判断当前t的第i位是否为 有效数据位
            {
                show_point_color(18 + (23 - i), 138 + j, 0);
            }
        }
    }
}
int main(void)
{
    open_lcd();
    bool hasOutputtedPackage = false;
    int x, y;
    show_bmp("./caidan.bmp");
    pthread_t t1, t2; // 创建两个保存线程号的变量
    int flag = 0;
    // 创建线程1
    flag = pthread_create(&t1, NULL, screen, NULL);
    if (flag != 0)
    {
        printf("thread_1 create failed\n");
        return 0;
    }
    // 创建线程2
    flag = pthread_create(&t2, NULL, seen, NULL);
    if (flag != 0)
    {
        printf("thread_2 create failed\n");
        return 0;
    }

    while (1)
    {
        if (ev.type == 1 && ev.code == 0x14a && ev.value == 1)
        {
            hasOutputtedPackage = false;
        }
        if (ev.type == 3 && ev.code == 0)
        {
            x = ev.value;
        }
        if (ev.type == 3 && ev.code == 1)
        {
            y = ev.value;
        }
        else if (x > 13 && x < 506 && y > 381 && y < 509 && !hasOutputtedPackage)
        {
            sleep(1);
            show_bmp("./caidan1.bmp");
            sleep(1);
            show_color(0, 20, 60, 200, orgian);
            hanzi_jie();
            hasOutputtedPackage = true;
        }

        else if (x > 732 && x < 823 && y > 356 && y < 390 && !hasOutputtedPackage)
        {
            strcpy(wbuf, "自煎一面黄");
            printf("自煎一面黄\n");
            hasOutputtedPackage = true;
        }

        else if (x > 432 && x < 480 && y > 316 && y < 404 && !hasOutputtedPackage)
        {
            strcpy(wbuf, "西兰花拌虾仁");
            printf("西兰花拌虾仁\n");
            hasOutputtedPackage = true;
        }

        else if (x > 110 && x < 182 && y > 316 && y < 404 && !hasOutputtedPackage)
        {
            strcpy(wbuf, "中式辣萝卜");
            printf("中式辣萝卜\n");
            hasOutputtedPackage = true;
        }

        else if (x > 750 && x < 800 && y > 50 && y < 110 && !hasOutputtedPackage)
        {
            strcpy(wbuf, "冰糖糯米藕");
            printf("冰糖糯米藕\n");
            hasOutputtedPackage = true;
        }

        else if (x > 463 && x < 512 && y > 50 && y < 110 && !hasOutputtedPackage)
        {
            strcpy(wbuf, "口水鸡");
            printf("口水鸡\n");
            hasOutputtedPackage = true;
        }

        else if (x > 100 && x < 208 && y > 50 && y < 110 && !hasOutputtedPackage)
        {
            strcpy(wbuf, "皮蛋豆腐");
            printf("皮蛋豆腐\n");
            hasOutputtedPackage = true;
        }
    }
    close_lcd();
    return 0;
}